﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;


namespace ConsoleApplication1
{
    class Program
    {
        static void Info()
        {
            Console.WriteLine("--------------------------------------------------------------------------------");
            Console.WriteLine("\t\t\tКОНСОЛЬНЫЙ КАЛЬКУЛЯТОР");
            Console.WriteLine("--------------------------------------------------------------------------------");
            Console.WriteLine("-Поддерживаются операции сложения, вычитания, умножения и деления");
            Console.WriteLine("-Приоритеты можно изменять с помощью скобок");
            Console.WriteLine("-Дробная часть отделяется запятой");
            Console.WriteLine("-Пример использования: введите, например, (2+8)*2-100/25 и нажмите Enter");
            Console.WriteLine("-Для использование результата последнего вычисления используйте знак @");
            Console.WriteLine("-Существует возможность сохранять промежуточные вычисления в переменные. Имя переменной может состоять из букв английского алфавита и цифр");
            Console.WriteLine("--------------------------------------------------------------------------------");
        }
        static void Main(string[] args)
        {
            Info();
            Calculator calculator = new Calculator();
            while (true)
            {
                string input1 = Console.ReadLine();
                string input = input1.Replace(" ", "");

                if (input == "q")
                {
                    Console.WriteLine();
                    break;
                }

                ExpressionState es = calculator.Calc(input);
                if (es == ExpressionState.InvalidBracket)
                {
                    Console.WriteLine();
                    Console.WriteLine("Invalid bracket use");
                    Console.WriteLine();
                }
                else if (es == ExpressionState.InvalidOperand)
                {
                    Console.WriteLine();
                    Console.WriteLine("Enter valid operands");
                    Console.WriteLine();
                }
                else if (es == ExpressionState.InvalidOperator)
                {
                    Console.WriteLine();
                    Console.WriteLine("Invalid use of operators");
                    Console.WriteLine();

                }
                else if (es == ExpressionState.EmptyEntry)
                {
                    Console.WriteLine();
                    Console.WriteLine("Empty entry");
                    Console.WriteLine();
                }
                else if (es == ExpressionState.InvalidVariableName)
                {
                    Console.WriteLine();
                    Console.WriteLine("Enter valid variable name: use upper or lower case letters and digits. At first position use letters only.");
                    Console.WriteLine();
                }
                else if (es == ExpressionState.NoVariableName)
                {
                    Console.WriteLine();
                    Console.WriteLine("Enter variable name to save last result");
                    Console.WriteLine();
                }

            }
        }
    }


    public class Calculator
    {
        static char[] ops = { '+', '*', '/', '-', '(', ')' };
        static char[] operators = { '+', '-', '*', '/' };
        string variable = "";

        Dictionary<string, double> vars = new Dictionary<string, double>();


        public ExpressionState Calc(string expression)
        {
            if (expression.StartsWith("store"))
            {
                variable = expression.Substring(5);
                if (variable.Length != 0)
                {
                    vars[variable] = vars["@"];
                    ExpressionState esVar = ValidateVariable(variable);
                    if (esVar == ExpressionState.InvalidVariableName)
                    {
                        return esVar;
                    }
                    Console.WriteLine();
                    Console.WriteLine("> you stored " + vars["@"] + " as " + variable);
                    Console.WriteLine();
                    return ExpressionState.Valid;
                }
                else
                {
                    return ExpressionState.NoVariableName;
                }
            }
            List<object> res = ParseStringInner(expression);
            Substitude(res);
            Check(res);
            ExpressionState es = Validate(res);
            if (es != ExpressionState.Valid)
            {
                return es;
            }
            DoMaths2(res, 0);
            vars["@"] = Convert.ToDouble(res[0]);
            Console.WriteLine();
            foreach (var el in res)
            {
                Console.WriteLine("> " + expression + " = " + el + " ");
                Console.WriteLine();
            }
            return ExpressionState.Valid;
        }
        void Substitude(List<object> res)
        {
            for (int i = 0; i < res.Count; i++)
            {
                string key = res[i].ToString();
                if (vars.ContainsKey(key))
                {
                    res[i] = vars[key];
                }
            }
        }

        void Check(List<object> res)
        {
            if (res.Count > 1)
            {
                NumberFormatInfo format = new NumberFormatInfo();
                format.NumberDecimalSeparator = ".";
                double number = 0;
                bool isNumber = Double.TryParse((res[1].ToString()), NumberStyles.AllowDecimalPoint, format, out number);
                if (res[0].ToString() == "-" && isNumber)
                {
                    res[0] = -number;
                    res.RemoveAt(1);
                }
                for (int i = 0; i < res.Count - 1; i++)
                {
                    if (res[i].ToString() == "(")
                    {
                        if (res[i + 1].ToString() == "-")
                        {
                            NumberFormatInfo format1 = new NumberFormatInfo();
                            format1.NumberDecimalSeparator = ".";
                            double number2 = 0;
                            bool isNumber2 = Double.TryParse((res[i + 2].ToString()), NumberStyles.AllowDecimalPoint, format1, out number2);
                            if (isNumber2)
                            {
                                {
                                    res[i + 1] = -number2;
                                    res.RemoveAt(i + 2);
                                }
                            }
                        }
                    }
                }
            }
        }



        static ExpressionState Validate(List<object> res)
        {
            if (res.Count == 0)
            {
                return ExpressionState.EmptyEntry;
            }

            for (int j = 0; j < operators.Length; j++)
            {
                if (res[0].ToString() == operators[j].ToString())
                {
                    return ExpressionState.InvalidOperand;
                }
            }
            int countOpen = 0;
            int countClose = 0;
            for (int i = 0; i < res.Count; i++)
            {
                if (res[i].ToString() == "(")
                {
                    countOpen++;
                }
                else if (res[i].ToString() == ")")
                {
                    countClose++;
                }
                else if (countClose > countOpen)
                {
                    return ExpressionState.InvalidBracket;
                }
                if (!IsOp(res[i].ToString()))
                {
                    double number = 0;

                    NumberFormatInfo format = new NumberFormatInfo();
                    format.NumberDecimalSeparator = ".";

                    bool isNumber = Double.TryParse((res[i].ToString()), NumberStyles.AllowDecimalPoint, format, out number);
                    if (!isNumber)
                    {
                        return ExpressionState.InvalidOperand;
                    }
                }
            }
            if (countOpen != countClose)
            {
                return ExpressionState.InvalidBracket;
            }
            for (int n = 0; n < res.Count - 1; n++)
            {
                for (int m = 0; m < operators.Length; m++)
                {
                    for (int k = 0; k < operators.Length; k++)
                    {
                        if (res[n].ToString() == operators[m].ToString())
                        {
                            if (res[n + 1].ToString() == operators[k].ToString())
                            {
                                return ExpressionState.InvalidOperator;
                            }
                        }
                    }
                }
                if (res[n].ToString() == "(" && res[n + 1].ToString() == ")")
                {
                    return ExpressionState.InvalidOperand;
                }
            }

            return ExpressionState.Valid;
        }

        static ExpressionState ValidateVariable(string sub)
        {
            if (!Char.IsLetter(sub[0]))
            {
                return ExpressionState.InvalidVariableName;
            }
            for (int i = 1; i < sub.Length; i++)
            {
                if (!Char.IsLetter(sub[i]) && !Char.IsDigit(sub[i]))
                {
                    return ExpressionState.InvalidVariableName;
                }
            }
            return ExpressionState.Valid;
        }


        static List<object> ParseStringInner(string input)
        {
            List<object> res = new List<object>();
            string s = "";
            for (int i = 0; i < input.Length; i++)
            {
                char curChar = input[i];
                bool isOperator = IsOp(input[i]);

                if (!isOperator)
                {
                    s = s + curChar;
                }
                else
                {
                    if (s.Length != 0)
                    {
                        res.Add(s);
                        s = "";
                    }
                    res.Add(curChar);
                }
            }
            if (s.Length != 0)
            {
                res.Add(s);
            }
            return res;
        }


        static bool IsOp(char c)
        {
            for (int i = 0; i < ops.Length; i++)
            {
                if (c == ops[i])
                {
                    return true;
                }
            }
            return false;
        }


        static bool IsOp(string s)
        {
            for (int i = 0; i < ops.Length; i++)
            {
                if (s == ops[i].ToString())
                {
                    return true;
                }
            }
            return false;
        }

        static void DoMaths2(List<object> res, int from)
        {
            int braceEnd = -1;
            for (int i = from; i < res.Count; i++)
            {
                if (res[i].ToString() == "(")
                {
                    DoMaths2(res, i + 1);
                    res.RemoveAt(i);
                }
                if (res[i].ToString() == ")")
                {
                    braceEnd = i;
                    res.RemoveAt(braceEnd);
                    break;
                }
            }
            if (braceEnd == -1)
            {
                DoMathsSimple(res, from, res.Count);
            }
            else
            {
                DoMathsSimple(res, from, braceEnd - 1);
            }
        }

        static List<object> DoMathsSimple(List<object> res, int from, int to)
        {
            for (int i = from; i < Math.Min(res.Count, to); i++)
            {

                double tmp;
                if (res[i].ToString() == "*")
                {
                    tmp = Convert.ToDouble(res[i - 1]) * Convert.ToDouble(res[i + 1]);
                    res[i - 1] = tmp;
                    res.RemoveRange(i, 2);
                    i--;
                }
                else if (res[i].ToString() == "/")
                {
                    tmp = Convert.ToDouble(res[i - 1]) / Convert.ToDouble(res[i + 1]);
                    res[i - 1] = tmp;
                    res.RemoveRange(i, 2);
                    i--;
                }
            }
            for (int i = from; i < Math.Min(res.Count, to); i++)
            {
                double tmp;
                if (res[i].ToString() == "+")
                {
                    tmp = Convert.ToDouble(res[i - 1]) + Convert.ToDouble(res[i + 1]);
                    res[i - 1] = tmp;
                    res.RemoveRange(i, 2);
                    i--;
                }
                else if (res[i].ToString() == "-")
                {
                    tmp = Convert.ToDouble(res[i - 1]) - Convert.ToDouble(res[i + 1]);
                    res[i - 1] = tmp;
                    res.RemoveRange(i, 2);
                    i--;
                }
            }
            return res;
        }
    }


    public enum ExpressionState
    {
        InvalidBracket,
        InvalidOperator,
        InvalidOperand,
        EmptyEntry,
        InvalidVariableName,
        NoVariableName,
        Valid,
    }
}
